﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

using Microsoft.AspNetCore.Routing;
using Neto.Web.Listeners;

namespace Neto.Web;

public static class WebExtensions
{
    /// <summary>
    ///     获取当前域名(http://xxx.com)
    /// </summary>
    /// <param name="httpContext">当前http上下文</param>
    /// <returns></returns>
    public static string GetDomain(this HttpContext? httpContext)
    {
        if (httpContext == null) return "";
        return new StringBuilder()
            .Append(httpContext.Request.Scheme)
            .Append("://")
            .Append(httpContext.Request.Host.Value).ToString();
    }

    /// <summary>
    ///     获取不包含Host的url部分(/home/index?abc=12312)
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetUrlNoHost(this HttpContext httpContext)
    {
        return new StringBuilder()
            //.Append(request.Scheme)
            //.Append("://")
            //.Append(request.Host)
            .Append(httpContext.Request.PathBase)
            .Append(httpContext.Request.Path)
            .Append(httpContext.Request.QueryString)
            .ToString();
    }

    /// <summary>
    ///     获取不包含Host的url部分(http://xxx.com/home/index?abc=12312)
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetUrl(this HttpContext httpContext)
    {
        return new StringBuilder()
            .Append(httpContext.Request.Scheme)
            .Append("://")
            .Append(httpContext.Request.Host)
            .Append(httpContext.Request.PathBase)
            .Append(httpContext.Request.Path)
            .Append(httpContext.Request.QueryString)
            .ToString();
    }

    /// <summary>
    ///     获取路由的Area
    /// </summary>
    /// <param name="httpContent"></param>
    /// <returns></returns>
    public static string GetRouteArea(this HttpContext httpContent)
    {
        return httpContent.Request?.RouteValues["area"]?.ToString() ?? "";
    }

    /// <summary>
    ///     设置通过appsettings 可以下载文件类型 扩展
    ///     此扩展需要在  app.UseStaticFiles() 之前使用
    /// </summary>
    /// <param name="app"></param>
    public static void UseCustomStaticFile(this IApplicationBuilder app)
    {
        var mimes = App.GetConfig<Dictionary<string, string>>("StaticFileOptions:FileExtensionContentType");
        var serveUnknownFileTypes = App.GetConfig<bool>("StaticFileOptions:ServeUnknownFileTypes");
        var physicalFilePath = App.GetConfig<string>("StaticFileOptions:PhysicalFilePath");

        //var a = new PhysicalFileProvider(Directory.GetCurrentDirectory()); ;

        var options = new StaticFileOptions
        {
            //FileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory()),
            //设置不限制content-type 该设置可以下载所有类型的文件，但是不建议这么设置，因为不安全
            //ServeUnknownFileTypes = true
            //下面设置可以下载apk和nupkg类型的文件
            //ContentTypeProvider = new FileExtensionContentTypeProvider(new Dictionary<string, string>
            //{
            //  { ".apk","application/vnd.android.package-archive"},
            //  //{ ".nupkg","application/zip"}
            //})
            ServeUnknownFileTypes = serveUnknownFileTypes
        };
        if (mimes != null && mimes.Count != 0)
            options.ContentTypeProvider = new FileExtensionContentTypeProvider(mimes);
        if (!string.IsNullOrWhiteSpace(physicalFilePath) && Directory.Exists(physicalFilePath))
            options.FileProvider = new PhysicalFileProvider(physicalFilePath);
        app.UseStaticFiles(options);
        app.UseStaticFiles(); //可重复设置，设置后不会异常
        
        //注册全局Web日志接口
        InvokeHelper.Register(typeof(IWebAccessLogListener), "Log",5);
    }

    ///// <summary>
    ///// 获取访问信息
    ///// </summary>
    ///// <param name="httpContext"></param>
    ///// <returns></returns>
    //public static string GetWebVisitInfo(this HttpContext httpContext)
    //{
    //    if (httpContext == null) return "";
    //    var logcontent = new StringBuilder();
    //    logcontent.AppendLine($"  访问地址： {httpContext.Request.Path}{httpContext.Request.QueryString}");
    //    var ipv4 = httpContext.GetRemoteIpAddressToIPv4();
    //    var ipv6 = httpContext.GetRemoteIpAddressToIPv6();
    //    logcontent.AppendLine($"IPV4: {ipv4}    IPV6: {ipv6}");

    //    var headers = httpContext.Request.Headers.ToDictionary(x => x.Key, v => v.Value.ToString());
    //    logcontent.AppendLine($"Header：{headers.ToJson()}");

    //    //var area = context.RouteData.Values["area"]?.ToString() ?? "";
    //    //var controller = context.RouteData.Values["controller"]?.ToString() ?? "";
    //    //var action = actionContext.RouteData.Values["action"]?.ToString() ?? "";
    //    // method ：OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法
    //    try
    //    {
    //        if (httpContext.Request.Method.ToLower() != "get")
    //        {
    //            if (httpContext.Request.HasFormContentType)
    //            {
    //                var form = httpContext.Request.Form;
    //                logcontent.AppendLine($"Form 参数：{form.ToDictionary(x => x.Key, v => v.Value.ToString()).ToJson()}");
    //            }
    //            else
    //            {
    //            }
    //        }
    //    }
    //    catch (Exception ex)
    //    {
    //        var _logger = AppX.GetSeriLogger("WebActionFilter");
    //        _logger.Error(ex.ToStringEx());
    //    }
    //    //URL  参数
    //    if (httpContext.Request.Query.Any())
    //    {
    //        var urlpars = httpContext.Request.Query.ToDictionary(x => x.Key, v => v.Value.ToString());
    //        logcontent.AppendLine($"URL 参数：{urlpars.ToJson()}");
    //    }

    //    return logcontent.ToString();
    //}

    ///// <summary>
    ///// 获取访问信息
    ///// </summary>
    ///// <param name="httpContext"></param>
    ///// <returns></returns>
    //public static void GetWebVisitInfo(this ActionExecutingContext context, LogVisitEntity logVisitEntity)
    //{
    //    if (context == null) return;
    //    if (logVisitEntity == null) logVisitEntity = new();

    //    context.HttpContext.GetWebVisitInfo(logVisitEntity);
    //    //logVisitEntity.Area = context.RouteData.Values["area"]?.ToString() ?? "";
    //    //logVisitEntity.Controller = context.RouteData.Values["controller"]?.ToString() ?? "";
    //    //logVisitEntity.Action = context.RouteData.Values["action"]?.ToString() ?? "";
    //}

    /// <summary>
    ///     获取webHttp访问信息
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static WebHttpInfo? GetWebInfo(this HttpContext? httpContext)
    {
        if (httpContext == null) return null;
        var webHttpInfo = new WebHttpInfo();

        #region Request

        //HttpRequest 对象
        var httpRequest = httpContext.Request;

        webHttpInfo.Request.ContentLength = httpRequest.ContentLength;
        webHttpInfo.Request.ContentType = httpRequest.ContentType;
        webHttpInfo.Request.Cookies = Uri.UnescapeDataString(httpRequest.Headers["cookie"].ToString());

        // method ：OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法
        try
        {
            if (httpContext.Request.Method.ToLower() != "get" && httpContext.Request.HasFormContentType)
            {
                var form = httpContext.Request.Form;
                //Form 参数：
                webHttpInfo.Request.Form = $"{form.ToDictionary(x => x.Key, v => v.Value).ToJson()}";
            }
        }
        catch (Exception ex)
        {
            LogX.Error(ex, "WebActionFilter");
        }

        webHttpInfo.Request.HasFormContentType = httpRequest.HasFormContentType;
        webHttpInfo.Request.Headers =
            httpContext?.Request?.Headers?.ToDictionary(x => x.Key, v => v.Value.ToString())?.ToJson() ?? "";
        webHttpInfo.Request.Host = httpRequest.Host.Host;
        webHttpInfo.Request.IsHttps = httpRequest.IsHttps;
        webHttpInfo.Request.Method = httpRequest.Method;
        webHttpInfo.Request.Path = httpRequest.Path;
        webHttpInfo.Request.PathBase = httpRequest.PathBase;
        webHttpInfo.Request.Protocol = httpRequest.Protocol;
        //URL  参数
        if (httpContext.Request.Query.Any())
        {
            var urlpars = httpContext.Request.Query.ToDictionary(x => x.Key, v => v.Value.ToString());
            //URL 参数：
            webHttpInfo.Request.QueryString = $"{urlpars.ToJson()}";
        }

        webHttpInfo.Request.RouteValues = httpRequest.RouteValues.ToDictionary(x => x.Key, v => v.Value).ToJson();
        webHttpInfo.Request.Scheme = httpRequest.Scheme;

        #endregion Request

        #region Response

        if (webHttpInfo.Response != null)
        {
            webHttpInfo.Response.HasStarted = httpContext.Response.HasStarted;
            webHttpInfo.Response.ContentType = httpContext.Response.ContentType;
            webHttpInfo.Response.ContentLength = httpContext.Response.ContentLength;
            webHttpInfo.Response.StatusCode = httpContext.Response.StatusCode;
            webHttpInfo.Response.Cookies = httpContext.Response.Cookies.ToDictionary().ToJson();
        }

        #endregion Response

        #region Connection

        webHttpInfo.Connection.Id = httpContext.Connection.Id;
        webHttpInfo.Connection.RemoteIpAddress = httpContext.Connection.RemoteIpAddress?.ToString();
        webHttpInfo.Connection.RemotePort = httpContext.Connection.RemotePort;
        webHttpInfo.Connection.LocalIpAddress = httpContext.Connection.LocalIpAddress?.ToString();
        webHttpInfo.Connection.ClientCertificate = httpContext.Connection.ClientCertificate?.ToString();

        #endregion Connection

        #region Request 扩展

        webHttpInfo.RequestEx.RemoteIPv4 = httpContext.GetRemoteIpAddressToIPv4();
        webHttpInfo.RequestEx.LocalIPv4 = httpContext.GetLocalIpAddressToIPv4();
        webHttpInfo.RequestEx.RemoteIPv6 = httpContext.GetRemoteIpAddressToIPv6();
        webHttpInfo.RequestEx.LocalIPv6 = httpContext.GetLocalIpAddressToIPv6();

        //请求的 Url 地址
        webHttpInfo.RequestEx.RequestUrl = Uri.UnescapeDataString(httpRequest.GetRequestUrlAddress());

        webHttpInfo.RequestEx.AreaName = httpContext.GetAreaName();
        webHttpInfo.RequestEx.ControllerName = httpContext.GetControllerName();
        webHttpInfo.RequestEx.ActionName = httpContext.GetActionName();

        // 获取来源 Url 地址
        webHttpInfo.RequestEx.RefererUrl = Uri.UnescapeDataString(httpRequest.GetRefererUrlAddress());

        //var logcontent = $"访问地址：{httpContext.Request.Path}{httpContext.Request.QueryString}";
        //var ipv4 = httpContext.GetRemoteIpAddressToIPv4();
        //var ipv6 = httpContext.GetRemoteIpAddressToIPv6();
        //logcontent += $"IPV4:{ipv4}    IPV6:{ipv6}";

        //var headers = httpContext.Request.Headers.ToDictionary(x => x.Key, v => v.Value.ToString());
        //logcontent += $"Header：{System.Text.Json.JsonSerializer.Serialize(headers, AppX.GetDefaultJsonSerializerOptions)}";

        //浏览器信息
        //#if NET5
        //            var useragent = httpContext?.Request?.Headers["User-Agent"].To<string>();
        //#elif NET6 || NET7 || NET8
        //           var useragent = httpContext?.Request?.Headers?.UserAgent.ToString();
        //#endif

        // 客户端浏览器信息
        webHttpInfo.RequestEx.UserAgent = httpRequest.Headers["User-Agent"];

        if (!string.IsNullOrWhiteSpace(webHttpInfo.RequestEx.UserAgent))
        {
            // var s = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36";
            // 获取具有嵌入正则表达式模式的解析器
            var uaParser = Parser.GetDefault();

            // get a parser using externally supplied yaml definitions
            // var uaParser = Parser.FromYaml(yamlString);
            //使用外部提供的yaml定义获取解析器
            var c = uaParser.Parse(webHttpInfo.RequestEx.UserAgent);
            webHttpInfo.RequestEx.Os = c.OS.ToString();
            webHttpInfo.RequestEx.Device = c.Device.ToString();
            webHttpInfo.RequestEx.Browser = c.UA.Family;
        }

        // 客户端请求区域语言
        webHttpInfo.RequestEx.AcceptLanguage = httpRequest.Headers["accept-language"];

        // 请求来源（swagger还是其他）
        webHttpInfo.RequestEx.RequestFrom = httpRequest.Headers["request-from"].ToString();
        webHttpInfo.RequestEx.RequestFrom = string.IsNullOrWhiteSpace(webHttpInfo.RequestEx.RequestFrom)
            ? "client"
            : webHttpInfo.RequestEx.RequestFrom;

        //屏幕
        httpRequest.Headers.TryGetValue("ScreenWidth", out var ScreenWidth);
        webHttpInfo.RequestEx.ScreenWidth = ScreenWidth.ToString();

        httpRequest.Headers.TryGetValue("ScreenHeight", out var ScreenHeight);
        webHttpInfo.RequestEx.ScreenHeight = ScreenHeight.ToString();

        // 获取授权用户
        var user = httpContext.User;

        //// 判断是否是授权访问
        //var isAuth = actionMethod.GetFoundAttribute<AllowAnonymousAttribute>(true) == null
        //    && httpContext.User != null
        //    && httpContext.User.Identity.IsAuthenticated;

        // 获取响应头信息
        webHttpInfo.RequestEx.AccessToken = httpContext.Response.Headers["access-token"].ToString();
        webHttpInfo.RequestEx.Authorization = string.IsNullOrWhiteSpace(webHttpInfo.RequestEx.AccessToken)
            ? httpRequest.Headers["Authorization"].ToString()
            : "Bearer " + webHttpInfo.RequestEx.AccessToken;

        // 获取响应 cookies 信息
        webHttpInfo.RequestEx.ResponseHeaderCookies =
            Uri.UnescapeDataString(httpContext.Response.Headers["Set-Cookie"].ToString());

        #endregion Request 扩展

        // 获取系统信息
        webHttpInfo.ServerInfo.osDescription = RuntimeInformation.OSDescription;
        webHttpInfo.ServerInfo.osArchitecture = RuntimeInformation.OSArchitecture.ToString();
        webHttpInfo.ServerInfo.frameworkDescription = RuntimeInformation.FrameworkDescription;
        var basicFrameworkDescription = typeof(App).Assembly.GetName();
        webHttpInfo.ServerInfo.basicFramework = basicFrameworkDescription.Name;
        webHttpInfo.ServerInfo.basicFrameworkVersion = basicFrameworkDescription.Version?.ToString();

        // 获取启动信息
        webHttpInfo.ServerInfo.EntryAssemblyName = Assembly.GetEntryAssembly().GetName().Name;

        // 获取进程信息
        var process = Process.GetCurrentProcess();
        webHttpInfo.ServerInfo.ProcessName = process.ProcessName;

        // 获取部署程序
        webHttpInfo.ServerInfo.DeployServer =
            webHttpInfo.ServerInfo.ProcessName == webHttpInfo.ServerInfo.EntryAssemblyName
                ? "Kestrel"
                : webHttpInfo.ServerInfo.ProcessName;

        // 服务器环境
        webHttpInfo.ServerInfo.Environment =
            httpContext.RequestServices.GetRequiredService<IWebHostEnvironment>().EnvironmentName;
        return webHttpInfo;
    }

    /// <summary>
    ///     获取webHttp访问信息
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="stringBuilder"></param>
    /// <returns></returns>
    public static StringBuilder GetWebInfoToString(this HttpContext httpContext, StringBuilder? stringBuilder = null)
    {
        stringBuilder ??= new StringBuilder();
        var webHttpInfo = httpContext.GetWebInfo();
        if (webHttpInfo == null) return stringBuilder;
        stringBuilder = webHttpInfo.GetWebInfoToString(stringBuilder) ;
        return stringBuilder;
    }
    
    
    /// <summary>
    ///     获取webHttp访问信息
    /// </summary>
    /// <param name="webHttpInfo"></param>
    /// <param name="stringBuilder"></param>
    /// <returns></returns>
    public static StringBuilder GetWebInfoToString(this WebHttpInfo? webHttpInfo, StringBuilder? stringBuilder = null)
    {
        stringBuilder ??= new StringBuilder();
        if (webHttpInfo == null) return stringBuilder;
        var monitorItems = new List<string>
        {
            $"##路由信息## [area]: {webHttpInfo.RequestEx.AreaName}; [controller]: {webHttpInfo.RequestEx.ControllerName}; [action]: {webHttpInfo.RequestEx.ActionName}",
            $"##请求方式## {webHttpInfo.Request.Method}",
            $"##请求地址## {webHttpInfo.RequestEx.RequestUrl}",
            $"##来源地址## {webHttpInfo.RequestEx.RefererUrl}",
            $"##请求端源## {webHttpInfo.RequestEx.RequestFrom}",
            $"##浏览器标识## {webHttpInfo.RequestEx.UserAgent}",
            $"##客户端区域语言## {webHttpInfo.RequestEx.AcceptLanguage}",
            $"##客户端 IP 地址## {webHttpInfo.RequestEx.RemoteIPv4}",
            $"##服务端 IP 地址## {webHttpInfo.RequestEx.LocalIPv4}"
            //, $"##客户端连接 ID## {traceId}"
            //, $"##服务线程 ID## #{threadId}"
            //, $"##执行耗时## {timeOperation.ElapsedMilliseconds}ms"
            ,
            "━━━━━━━━━━━━━━━  Cookies ━━━━━━━━━━━━━━━",
            $"##请求端## {webHttpInfo.Request.Cookies}",
            $"##响应端## {webHttpInfo.RequestEx.ResponseHeaderCookies}",
            "━━━━━━━━━━━━━━━  系统信息 ━━━━━━━━━━━━━━━",
            $"##系统名称## {webHttpInfo.ServerInfo.osDescription}",
            $"##系统架构## {webHttpInfo.ServerInfo.osArchitecture}",
            $"##基础框架## {webHttpInfo.ServerInfo.basicFramework} v{webHttpInfo.ServerInfo.basicFrameworkVersion}",
            $"##.NET 架构## {webHttpInfo.ServerInfo.frameworkDescription}",
            "━━━━━━━━━━━━━━━  启动信息 ━━━━━━━━━━━━━━━",
            $"##运行环境## {webHttpInfo.ServerInfo.Environment}",
            $"##启动程序集## {webHttpInfo.ServerInfo.EntryAssemblyName}",
            $"##进程名称## {webHttpInfo.ServerInfo.ProcessName}",
            $"##托管程序## {webHttpInfo.ServerInfo.DeployServer}"
        };
        foreach (var item in monitorItems) stringBuilder.AppendLine(item);
        App.HttpContext.GetJWTInfo(webHttpInfo, stringBuilder);
        return stringBuilder;
    }
    
    

    /// <summary>
    ///     生成 JWT授权日志信息
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="webHttpInfo"></param>
    /// <param name="stringBuilder"></param>
    /// <returns></returns>
    public static StringBuilder GetJWTInfo(this HttpContext httpContext, WebHttpInfo webHttpInfo,
        StringBuilder? stringBuilder = null)
    {
        stringBuilder ??= new StringBuilder("━━━━━━━━━━━━━━━  授权信息 ━━━━━━━━━━━━━━━");
        stringBuilder.AppendLine($"##JWT Token## {webHttpInfo.RequestEx.Authorization}");
        if (!httpContext.User.Claims.Any()) return stringBuilder;

        foreach (var claim in httpContext.User.Claims)
        {
            var valueType = claim.ValueType.Replace("http://www.w3.org/2001/XMLSchema#", "");
            var value = claim.Value;

            // 解析时间戳并转换
            if (!string.IsNullOrEmpty(value) && claim.Type is "iat" or "nbf" or "exp")
            {
                var succeed = long.TryParse(value, out var seconds);
                if (succeed)
                    value =
                        $"{value} ({DateTimeOffset.FromUnixTimeSeconds(seconds).ToLocalTime():yyyy-MM-dd HH:mm:ss:ffff(zzz) dddd} L)";
            }

            stringBuilder.AppendLine($"##{claim.Type} ({valueType})## {value}");
        }

        return stringBuilder;
    }

    /// <summary>
    ///     获取IPV4
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="xff">是否优先取 X-Forwarded-For</param>
    /// <returns></returns>
    public static string GetIpV4(this HttpContext httpContext, bool xff = false)
    {
        return httpContext.GetRemoteIpAddressToIPv4(xff);
    }

    /// <summary>
    ///     获取IPV4
    /// </summary>
    /// <param name="httpContextAccessor"></param>
    /// <param name="xff">是否优先取 X-Forwarded-For</param>
    /// <returns></returns>
    public static string GetIpV4(this IHttpContextAccessor httpContextAccessor, bool xff = false)
    {
        return httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4(xff);
    }

    /// <summary>
    ///     获取IPV6
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetIpV6(this HttpContext httpContext)
    {
        return httpContext.GetRemoteIpAddressToIPv6();
    }

    /// <summary>
    ///     获取IPV6
    /// </summary>
    /// <param name="httpContextAccessor"></param>
    /// <returns></returns>
    public static string GetIpV6(this IHttpContextAccessor httpContextAccessor)
    {
        return httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv6();
    }

    /// <summary>
    ///     获取文件
    /// </summary>
    /// <param name="logicalPath">逻辑路径（假路径，不是真的，防止从url直接访问到文件）</param>
    /// <returns></returns>
    //[Route("/u/f/{name?}/{name2?}/{name3?}/{name4?}/{name5?}/{name6?}/{name7?}/{name8?}/{name9?}/{name10?}")]
    //[UploadFileFilter]
    public static IActionResult GetFile(this Controller controller, string defaultPhysicalPath,
        string logicalPath = "/u/f", string baseFloder = "upload") //要下载文件码
    {
        var httpContext = App.HttpContext;

        ////图片防盗链 验证（基础认证，能防御基础的爬虫手段）
        ////var applicationUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host.Value}";
        //var headersDictionary = httpContext.Request.Headers;
        //var urlReferrer = headersDictionary[HeaderNames.Referer].ToString()??"";

        //var domain = httpContext.GetDomain();

        //if (!string.IsNullOrEmpty(urlReferrer) && !urlReferrer.StartsWith(domain))
        //{
        //   //返回默认图片，提示
        //}

        var pathurl = Path.Combine(httpContext.Request.Path, httpContext.Request.QueryString.Value ?? "");
        //默认物理文件夹
        //var defaultPhysicalPath = App.Configuration["WebUploadSetting:WebUploadFloder"];
        if (string.IsNullOrWhiteSpace(defaultPhysicalPath) || !Directory.Exists(defaultPhysicalPath))
            //如果不存在，设置默认的物理文件，网站更目录下
            defaultPhysicalPath = AppX.GetUploadDefaultFloder();
        var filepath = Path.Combine(defaultPhysicalPath, baseFloder, $"{pathurl.TrimStart(logicalPath)}");

        if (!File.Exists(filepath)) return controller.Content("");
        var stream = File.OpenRead(filepath); //创建文件流
        return controller.File(stream, filepath.GetFileMimeType());
    }

    /// <summary>
    ///     获取文件
    /// </summary>
    /// <param name="relativePath">逻辑路径（假路径，不是真的，防止从url直接访问到文件）</param>
    /// <returns></returns>
    //[Route("/u/f/{name?}/{name2?}/{name3?}/{name4?}/{name5?}/{name6?}/{name7?}/{name8?}/{name9?}/{name10?}")]
    //[UploadFileFilter]
    public static IActionResult
        GetDbFile(this Controller controller, string defaultPhysicalPath, string relativePath) //要下载文件码
    {
        //var httpContext = App.HttpContext;

        ////图片防盗链 验证（基础认证，能防御基础的爬虫手段）
        ////var applicationUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host.Value}";
        //var headersDictionary = httpContext.Request.Headers;
        //var urlReferrer = headersDictionary[HeaderNames.Referer].ToString()??"";

        //var domain = httpContext.GetDomain();

        //if (!string.IsNullOrEmpty(urlReferrer) && !urlReferrer.StartsWith(domain))
        //{
        //   //返回默认图片，提示
        //}

        //var pathurl = Path.Combine(httpContext.Request.Path, httpContext.Request.QueryString.Value ?? "");
        //默认物理文件夹
        //var defaultPhysicalPath = App.Configuration["WebUploadSetting:WebUploadFloder"];
        if (string.IsNullOrWhiteSpace(defaultPhysicalPath) || !Directory.Exists(defaultPhysicalPath))
            //如果不存在，设置默认的物理文件，网站更目录下
            defaultPhysicalPath = AppX.GetUploadDefaultFloder();
        var filepath = Path.Combine(defaultPhysicalPath, "upload", $"{relativePath.TrimStart('/').TrimStart('\\')}");

        if (!File.Exists(filepath)) return controller.Content("");
        var stream = File.OpenRead(filepath); //创建文件流
        return controller.File(stream, filepath.GetFileMimeType());
    }

    /// <summary>
    ///     登录跳转地址验证
    /// </summary>
    /// <param name="returnUrl"></param>
    /// <param name="defaultReturnUrl">如果验证失败，默认的跳转地址</param>
    /// <returns></returns>
    public static string LoginReturnUrlVer(this string returnUrl, string defaultReturnUrl)
    {
        var domain = App.HttpContext.GetDomain().TrimEnd("/").ToLower();
        //跳转地址为空
        if (string.IsNullOrWhiteSpace(returnUrl))
        {
            if (defaultReturnUrl.ToLower().StartsWith(domain))
                return defaultReturnUrl;
            return domain.ToLower() + "/" + defaultReturnUrl.TrimStart("/");
        }

        //跳转地址不包含当前域名
        if (returnUrl.ToLower().StartsWith(domain))
            return returnUrl;
        //使用默认地址
        if (defaultReturnUrl.ToLower().StartsWith(domain))
            return defaultReturnUrl;
        return domain.ToLower() + "/" + defaultReturnUrl.TrimStart("/");
    }


    /// <summary>
    ///     获取登录用户ID
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static long GetLoginUserId(this HttpContext httpContext)
    {
        return httpContext.GetLoginUserId<long>();
    }

    /// <summary>
    ///     获取用户 Id
    /// </summary>
    public static T? GetLoginUserId<T>(this ClaimsPrincipal? user)
    {
        if (user == null || user?.Identity?.IsAuthenticated == false) return default;
        if (user?.Claims?.Any(o => o.Type == ClaimConst.Claim_UserId) ?? false)
        {
            var v = user?.FindFirst(ClaimConst.Claim_UserId)?.Value;
            return v.To<T>();
        }

        return default;
    }

    /// <summary>
    ///     获取用户 Id
    /// </summary>
    public static T? GetLoginUserId<T>(this HttpContext httpContext)
    {
        return GetLoginUserId<T>(httpContext?.User ?? null);
    }

    /// <summary>
    ///     获取用户 UserNick
    /// </summary>
    public static string GetLoginUserNick(this ClaimsPrincipal? user)
    {
        if (user == null || user.Identity?.IsAuthenticated == false) return "";
        if (user.Claims.Any(o => o.Type == ClaimConst.Claim_Name))
        {
            var v = user?.FindFirst(ClaimConst.Claim_Name)?.Value;
            return v.To<string>();
        }

        return user.GetLoginUserAccount();
    }

    /// <summary>
    ///     获取用户 UserNick
    /// </summary>
    public static string GetLoginUserNick(this HttpContext httpContext)
    {
        return httpContext.User.GetLoginUserNick();
    }

    /// <summary>
    ///     获取用户 账户
    /// </summary>
    public static string GetLoginUserAccount(this ClaimsPrincipal? user)
    {
        //return httpContext.User.Identity.Name;
        return user?.GetUserValue<string>(ClaimConst.Claim_Account) ?? "";
    }

    // <summary>
    /// 获取用户 账户
    /// </summary>
    public static string GetLoginUserAccount(this HttpContext httpContext)
    {
        //return httpContext.User.Identity.Name;
        return httpContext?.GetUserValue<string>(ClaimConst.Claim_Account) ?? "";
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <param name="user"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string GetUserValue(this ClaimsPrincipal user, string key)
    {
        return user?.GetUserValue<string>(key) ?? "";
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static string GetUserValue(this HttpContext httpContext, string key)
    {
        return httpContext.GetUserValue<string>(key) ?? "";
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="user"></param>
    /// <param name="key"></param>
    /// <returns>返回 TEntity</returns>
    public static TEntity? GetUserValue<TEntity>(this ClaimsPrincipal? user, string key)
    {
        if (user == null) return default;
        if (user.FindFirst(key) == null) return default;
        var claim = user.FindFirst(key);
        if (claim == null || string.IsNullOrEmpty(claim.Value)) return default;
        return claim.Value.To<TEntity>();
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <param name="httpContext"></param>
    /// <param name="key"></param>
    /// <returns>返回 TEntity</returns>
    public static TEntity? GetUserValue<TEntity>(this HttpContext httpContext, string key)
    {
        return GetUserValue<TEntity>(httpContext.User, key);
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="key"></param>
    /// <returns>返回 TEntity</returns>
    public static string? GetHeaderValue(this HttpContext httpContext, string key)
    {
        var state = httpContext.Request.Headers.TryGetValue(key, out var sv);
        if (state)
            return sv.ToString();
        return string.Empty;
    }

    /// <summary>
    ///     用获取值
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="key"></param>
    /// <returns>返回 TEntity</returns>
    public static T? GetHeaderValue<T>(this HttpContext httpContext, string key)
    {
        var state = httpContext.Request.Headers.TryGetValue(key, out var sv);
        if (state)
            return sv.ToString().To<T>();
        return default;
    }

    /// <summary>
    ///     判断是否包含 AllowAnonymousAttribute
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static bool HasAllowAnonymousAttribute(this object obj)
    {
        return obj.GetType().GetCustomAttributes(typeof(AllowAnonymousAttribute), false).Length > 0;
    }

    #region 获取Request的 ControllerName,ActionName,AreaName

    /// <summary>
    ///     从路由中获取Controller
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetControllerName(this HttpContext httpContext)
    {
        var controllerName = httpContext.Request.RouteValues["controller"]?.ToString() ?? "";
        return controllerName;
    }

    /// <summary>
    ///     从路由中获取Controller
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetActionName(this HttpContext httpContext)
    {
        var actionName = httpContext.Request.RouteValues["action"]?.ToString() ?? "";
        return actionName;
    }

    /// <summary>
    ///     从路由中获取Area
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    public static string GetAreaName(this HttpContext httpContext)
    {
        var areaName = httpContext.Request.RouteValues["area"]?.ToString() ?? "";
        return areaName;
    }

    /// <summary>
    ///     从路由中获取Controller
    /// </summary>
    /// <param name="actionContext"></param>
    /// <returns></returns>
    public static string GetControllerName(this ActionContext actionContext)
    {
        var controllerName = actionContext.RouteData.Values["controller"]?.ToString() ?? "";
        return controllerName;
    }

    /// <summary>
    ///     从路由中获取Action
    /// </summary>
    /// <param name="actionContext"></param>
    /// <returns></returns>
    public static string GetActionName(this ActionContext actionContext)
    {
        var actionName = actionContext.RouteData.Values["action"]?.ToString() ?? "";
        return actionName;
    }

    /// <summary>
    ///     从路由中获取Area
    /// </summary>
    /// <param name="actionContext"></param>
    /// <returns></returns>
    public static string GetAreaName(this ActionContext actionContext)
    {
        var areaName = actionContext.RouteData.Values["area"]?.ToString() ?? "";
        return areaName;
    }

    #endregion
    
    
    /// <summary>
    /// 获取日志文件夹名称
    /// </summary>
    public static string GetLogFloderName(this HttpContext httpContext, RouteData routeData)
    {
        if (httpContext == null) return "WebAccessLog\\default";

        var request = httpContext.Request;
        if (request == null) return "WebAccessLog\\default";

        // 1. 尝试从请求路径中提取路径作为日志文件夹
        var path = request.Path.Value?.Trim('/');
        if (!string.IsNullOrEmpty(path))
        {
            // 如果路径不是根路径 "/"，则直接将路径中的 '/' 替换为 '\'
            return $"WebAccessLog\\{path.Replace('/', '\\')}";
        }

        // 2. 如果路径为空，则尝试从路由数据中获取 area/controller/action
        var area = routeData.Values["area"]?.ToString();
        var controller = routeData.Values["controller"]?.ToString();
        var action = routeData.Values["action"]?.ToString();

        var folderParts = new List<string>();

        if (!string.IsNullOrEmpty(area))
        {
            folderParts.Add(area);
        }

        if (!string.IsNullOrEmpty(controller))
        {
            folderParts.Add(controller);
        }

        if (!string.IsNullOrEmpty(action))
        {
            folderParts.Add(action);
        }

        if (folderParts.Count > 0)
        {
            return $"WebAccessLog\\{string.Join("\\", folderParts)}";
        }

        // 3. 默认情况
        return "WebAccessLog\\default";
    }
    
    /// <summary>
    /// 获取文件夹名称
    /// </summary>
    /// <param name="httpContext"></param>
    /// <param name="routeData"></param>
    /// <returns></returns>
    public static string GetLogFloderName2(this HttpContext httpContext, RouteData routeData)
    {
        
        var logFloderName = "default";

        //针对与路由是  api/app/order/add 这种情况，
        var urlPath = httpContext?.Request?.Path ?? "";
        if (!string.IsNullOrWhiteSpace(urlPath) && urlPath != "/")
        {
            logFloderName = httpContext?.Request?.Path.Value?.Replace("/", "\\");
        }
        else
        {
            var area = routeData.Values["area"]?.ToString() ?? "";
            var controller = routeData.Values["controller"]?.ToString() ?? "";
            var action = routeData.Values["action"]?.ToString() ?? "";

            if (!string.IsNullOrWhiteSpace(area))
                logFloderName = area;
            else
            {
                var path1 = $"/{area}/{controller}/{action}".Trim('/'); //  /test/test
                var path = httpContext?.Request?.Path.ToString(); //path:api/test/test
                if (path != null && path.Contains(path1))
                {
                    area = path.RemoveLastStr(path1);
                    if (!string.IsNullOrWhiteSpace(area)) logFloderName = area.Trim("//").Trim('/');
                }
            }

            if (!string.IsNullOrWhiteSpace(controller)) logFloderName += $"\\{controller}";

            logFloderName = logFloderName.Trim('\\');
        }

        return $"WebAccessLog/{logFloderName}";
    }
}